﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.TeamFoundation.Server;
using System.Security.Principal;
using Microsoft.TeamFoundation.VersionControl.Client;
using Microsoft.TeamFoundation.Client;
using System.Xml;
using System.IO;
using Microsoft.VisualStudio.Shell;
using Microsoft.VisualStudio.OLE.Interop;
using MSVSOleInterop = Microsoft.VisualStudio.OLE.Interop;
using EnvDTE;
using System.Runtime.InteropServices;
using Microsoft.VisualStudio.Shell.Interop;
using Microsoft.VisualStudio;
using System.Globalization;
using EnvDTE80;
using Microsoft.SharePoint;

namespace PowerShell.Host.PsTFS.CdmLet.PsObject
{
    public static class PsMethode
    {
        #region ADD/Remove member in TFS
        /// <summary>
        ///    Get the TFS Role Identity for the given rolename and project 
        /// </summary>
        /// <param name="roleName">Name of the role</param>
        /// <param name="nodeName">Name of the project</param>
        /// <returns>Identity object</returns>
        public static Identity GetRoleIdentity(string roleName, string nameprojet, TeamFoundationServer tfs, VersionControlServer versionControl, IGroupSecurityService sec)
        {

            ICommonStructureService css = (ICommonStructureService)tfs.GetService(typeof(ICommonStructureService));

            ProjectInfo projInfo = css.GetProjectFromName(nameprojet);
            Identity[] groups = sec.ListApplicationGroups(projInfo.Uri);
            Identity Role = null;

            foreach (Identity TFSRole in groups)
            {
                Identity TFSRoleGroup = sec.ReadIdentity(SearchFactor.Sid, TFSRole.Sid, QueryMembership.Direct);
                if (String.Compare(TFSRoleGroup.DisplayName, roleName, StringComparison.CurrentCultureIgnoreCase) == 0)
                {
                    Role = TFSRole;
                    break;
                }
            }

            if (Role == null)
            {
                throw new Exception( "ERROR" );
            }

            return Role;
        }
        /// <summary>
        ///   Add a user with given TFS role to specified Team Project
        /// </summary>
        /// <param name="userName">User Name</param>
        /// <param name="role">Role Name</param>
        /// <param name="nodeName">Project Name</param>
        public static void RemoveUserToRole(string userName, string role, string nodeName, TeamFoundationServer tfs, VersionControlServer versionControl, IGroupSecurityService sec)
        {

            Identity newMember = sec.ReadIdentityFromSource(SearchFactor.AccountName, userName);

            if (newMember == null)
            {
                throw new Exception("ERROR");
            }

            foreach (string rol in role.Split(';'))
            {
                Identity Role = GetRoleIdentity(rol, nodeName, tfs, versionControl, sec);

                try
                {
                    sec.RemoveMemberFromApplicationGroup(Role.Sid, newMember.Sid);

                    PsLog.AddLog(null, userName + " is remove in " + rol);
                }
                catch (Microsoft.TeamFoundation.Server.GroupSecuritySubsystemException ex)
                {
                   PsLog.AddLog(null, ex.Message);
                   Console.Error.WriteLine(ex.ToString());
                }
            }

        }
        /// <summary>
        ///   Add a user with given TFS role to specified Team Project
        /// </summary>
        /// <param name="userName">User Name</param>
        /// <param name="role">Role Name</param>
        /// <param name="nodeName">Project Name</param>
        public static void AddUserToRole(string userName, string role, string nodeName,TeamFoundationServer tfs, VersionControlServer versionControl, IGroupSecurityService sec)
        {
            
            Identity newMember = sec.ReadIdentityFromSource(SearchFactor.AccountName, userName);

            if (newMember == null)
            {
                throw new Exception("ERROR");
            }

            foreach (string rol in role.Split(';'))
            {
                Identity Role = GetRoleIdentity(rol, nodeName,tfs,versionControl,sec);

                try
                {
                    if (!IsUserExist(userName, sec))
                    {
                        sec.AddMemberToApplicationGroup(Role.Sid, newMember.Sid);

                        PsLog.AddLog(null, userName + " is add in " + rol);
                    }
                    else
                    {

                        PsLog.AddLog(null, userName + " is not add in " + rol);
                    }
                }
                catch (Microsoft.TeamFoundation.Server.GroupSecuritySubsystemException ex)
                {
                    PsLog.AddLog(null, ex.Message);
                    Console.Error.WriteLine( ex.Message);
                }
            }

        }

        /// <summary>
        ///    Check if the user exist on the TFS server.
        /// </summary>
        /// <param name="userName">Domain Name of the user</param>
        /// <returns>If the user exist on TFS it will return true else false</returns>
        public static bool IsUserExist(string userName,IGroupSecurityService sec)
        {
            if (String.IsNullOrEmpty(userName) == true)
                throw new ArgumentNullException(userName + " is requi");

            Identity tUser = sec.ReadIdentity(SearchFactor.AccountName, userName, QueryMembership.Direct);

            if (tUser == null)
                return false;
            else
                return true;
        }

        #endregion ADD/Remove member in TFS

        #region CreateProject

        private const string CEleadPackageGuid = "8E01EC3E-2928-4aa5-B720-E28C163818E6";
        private const string CVSProgramID = "VisualStudio.DTE.9.0";
        private const string CDTE2ObjectNotExistError = "System failed to create a new instance of Visual Studio and obtain its automation object.";
        private const string CDTEObjectNotExistError = "System failed to obtain the automation object of a running Visual Studio instance.";
        private const string COperationStartMessgae = "System has started to create your team project. This may take a while. Please wait ...";
        private const string CBatchTeamProjCreateCommand = "File.BatchNewTeamProject";
        private const string CPackageLoadError = "System failed to load the required package for team project creation.";
        private const string CIVsShellNotExistError = "System failed to access the Visual Studio Shell service";
        private const string CResultMessage = "The project creation log file with a name like {0} has been created at {1}, if you did not provide a log file folder. \nOtherwise, please find the log file at your_log_folder\\your_project_name.log";

        /// <summary>
        /// Création du projet d'équipe
        /// </summary>
        /// <param name="TFSName">Nom du serveur TFS</param>
        /// <param name="LogFolder">Emplacement des log</param>
        /// <param name="ProjectName">Nom du projet</param>
        /// <param name="ProjectSiteTitle">Tire du site web</param>
        /// <param name="ProjectSiteDescription">Description du site web</param>
        /// <param name="SccCreateType">Type de controle de code source</param>
        /// <param name="SccBranchFromPath">Si branche su un projet existant, indiquer ce projet</param>
        /// <param name="ProcessTemplateName">Nom du processtemplate</param>
        /// <returns></returns>
        public static bool CreateProject(string TFSName
            , string LogFolder
            , string ProjectName
            , string ProjectSiteTitle
            , string ProjectSiteDescription
            , string SccCreateType
            , string SccBranchFromPath
            , string ProcessTemplateName) 

        {

            XmlDocument xmldoc;
            XmlNode xmlnode;
            XmlElement xmlelem;
            XmlElement xmlelem2;
            XmlText xmltext;
            XmlAttribute xmlAtt;

            // Création du fichier XML de configuration
            xmldoc = new XmlDocument();
            xmlnode = xmldoc.CreateNode(XmlNodeType.XmlDeclaration, "", "");
            xmldoc.AppendChild(xmlnode);
            xmlelem = xmldoc.CreateElement("", "Project", "");
            xmlAtt = xmldoc.CreateAttribute("xmlns");
            xmlAtt.Value = "ProjectCreationSettingsFileSchema.xsd";
            xmlelem.Attributes.Append(xmlAtt);
            xmldoc.AppendChild(xmlelem);
            xmlelem2 = xmldoc.CreateElement("", "TFSName", "");
            xmltext = xmldoc.CreateTextNode(TFSName);
            xmlelem2.AppendChild(xmltext);
            xmldoc.ChildNodes.Item(1).AppendChild(xmlelem2);
            xmlelem2 = xmldoc.CreateElement("", "LogFolder", "");
            xmltext = xmldoc.CreateTextNode(LogFolder);
            xmlelem2.AppendChild(xmltext);
            xmldoc.ChildNodes.Item(1).AppendChild(xmlelem2);
            xmlelem2 = xmldoc.CreateElement("", "ProjectName", "");
            xmltext = xmldoc.CreateTextNode(ProjectName);
            xmlelem2.AppendChild(xmltext);
            xmldoc.ChildNodes.Item(1).AppendChild(xmlelem2);
            xmlelem2 = xmldoc.CreateElement("", "ProjectSiteTitle", "");
            xmltext = xmldoc.CreateTextNode(ProjectSiteTitle);
            xmlelem2.AppendChild(xmltext);
            xmldoc.ChildNodes.Item(1).AppendChild(xmlelem2);
            xmlelem2 = xmldoc.CreateElement("", "ProjectSiteDescription", "");
            xmltext = xmldoc.CreateTextNode(ProjectSiteDescription);
            xmlelem2.AppendChild(xmltext);
            xmldoc.ChildNodes.Item(1).AppendChild(xmlelem2);
            xmlelem2 = xmldoc.CreateElement("", "SccCreateType", "");
            xmltext = xmldoc.CreateTextNode(SccCreateType);
            xmlelem2.AppendChild(xmltext);
            xmldoc.ChildNodes.Item(1).AppendChild(xmlelem2);
            xmlelem2 = xmldoc.CreateElement("", "SccBranchFromPath", "");
            xmltext = xmldoc.CreateTextNode(SccBranchFromPath);
            xmlelem2.AppendChild(xmltext);
            xmldoc.ChildNodes.Item(1).AppendChild(xmlelem2);
            xmlelem2 = xmldoc.CreateElement("", "ProcessTemplateName", "");
            xmltext = xmldoc.CreateTextNode(ProcessTemplateName);
            xmlelem2.AppendChild(xmltext);
            xmldoc.ChildNodes.Item(1).AppendChild(xmlelem2);
            xmldoc.Save(@"c:/temp/pstfs.xml");
            DTE2 dte2 = null;

            try
            {
                // Création d'une instance de Visual studio
                dte2 = (DTE2)System.Activator.CreateInstance(System.Type.GetTypeFromProgID(CVSProgramID), true);

                if (null == dte2)
                {
                    PsLog.AddLog(CDTE2ObjectNotExistError);
                    Console.Error.WriteLine(CDTE2ObjectNotExistError);
                    return false;
                }

                DTE dte = dte2.DTE;
                if (null == dte)
                {
                    PsLog.AddLog(CDTEObjectNotExistError);
                    Console.Error.WriteLine(CDTEObjectNotExistError);
                    return false;
                }

                MSVSOleInterop.IServiceProvider sp = (MSVSOleInterop.IServiceProvider)dte;

                Guid serviceGuid = typeof(IVsShell).GUID;
                Guid interfaceGuid = typeof(IVsShell).GUID;
                IntPtr ppObj;

                sp.QueryService(ref serviceGuid, ref interfaceGuid, out ppObj);

                IVsShell iVsShell = Marshal.GetObjectForIUnknown(ppObj) as IVsShell;

                if (null != iVsShell)
                {
                    IVsPackage package = null;
                    Guid packageGuid = new Guid(CEleadPackageGuid);
                    int result = iVsShell.LoadPackage(ref packageGuid, out package);
                    if (result == VSConstants.S_OK && null != package)
                    {
                        PsLog.AddLog(COperationStartMessgae);
                        Console.WriteLine(COperationStartMessgae);
                        dte2.ExecuteCommand(CBatchTeamProjCreateCommand, @"c:/temp/pstfs.xml");
                    }
                    else
                    {
                        Console.WriteLine(CPackageLoadError);
                        PsLog.AddLog(CPackageLoadError);
                        return false;
                    }
                }
                else
                {
                    PsLog.AddLog(CIVsShellNotExistError);
                    Console.WriteLine(CIVsShellNotExistError);
                    return false;
                }

                PrintResultMessage();
            }
            catch (Exception ex)
            {
                System.Console.WriteLine(ex.ToString());
                PsLog.AddLog(null, ex.ToString());
            }
            finally
            {
                dte2.Quit();
            }
            
            return true;

        }

        private static void PrintResultMessage()
        {
            string logFileNamePattern = string.Format(CultureInfo.CurrentCulture, "{0}{1}.log", "ProjectCreation_", DateTime.Now.ToString("yyyy-MM-dd_hh.mm.ss", CultureInfo.CurrentCulture.DateTimeFormat));

            PsLog.AddLog(CResultMessage + logFileNamePattern + Path.GetTempPath());
            Console.WriteLine(CResultMessage,
                              logFileNamePattern, Path.GetTempPath());
        }

        #endregion CreateProject

        #region GetMembersProject
        /// <summary>
        /// Renvois la liste des membres d'un projet
        /// </summary>
        /// <param name="tfs"></param>
        /// <param name="ProjectName"></param>
        /// <returns></returns>
        public static List<PsGroups> GetMembersProject(TeamFoundationServer tfs, string ProjectName)
        {
            List<PsGroups> lg = new List<PsGroups>();
            // Get a reference to the version control service
            VersionControlServer versionControl = PsService.VCS(tfs);
            IGroupSecurityService sec = PsService.IGSS(tfs);

            TeamProject tp = versionControl.GetTeamProject(ProjectName);

            Identity[] appGroups = sec.ListApplicationGroups(tp.ArtifactUri.AbsoluteUri);
            foreach (Identity group in appGroups)
            {
                Identity[] groupMembers = sec.ReadIdentities(SearchFactor.Sid, new string[] { group.Sid }, QueryMembership.Expanded);
                foreach (Identity member in groupMembers)
                {
                    PsGroups pg = new PsGroups();
                    pg.AccountName = member.AccountName;
                    pg.Deleted = member.Deleted;
                    pg.Description = member.Description;
                    pg.DisplayName = member.DisplayName;
                    pg.DistinguishedName = member.DistinguishedName;
                    pg.Domain = member.Domain;
                    pg.MailAddress = member.MailAddress;
                    pg.MemberOf = member.MemberOf;
                    pg.SecurityGroup = member.SecurityGroup;
                    pg.Sid = member.Sid;
                    pg.SpecialType = member.SpecialType;
                    pg.Type = member.Type;
                    pg.Members = new List<PsMember>();
                    pg.TypeMember = TypeMember.TFS;

                    if (member.Members != null)
                    {
                        foreach (string memberSid in member.Members)
                        {
                            Identity memberInfo = sec.ReadIdentity(SearchFactor.Sid, memberSid, QueryMembership.None);
                            PsMember pm = new PsMember();
                            pm.AccountName = memberInfo.AccountName;
                            pm.Deleted = memberInfo.Deleted;
                            pm.Description = memberInfo.Description;
                            pm.DisplayName = memberInfo.DisplayName;
                            pm.DistinguishedName = memberInfo.DistinguishedName;
                            pm.Domain = memberInfo.Domain;
                            pm.MailAddress = memberInfo.MailAddress;
                            pm.MemberOf = memberInfo.MemberOf;
                            pm.SecurityGroup = memberInfo.SecurityGroup;
                            pm.Sid = memberInfo.Sid;
                            pm.SpecialType = memberInfo.SpecialType;
                            pm.Type = memberInfo.Type;
                            pg.Members.Add(pm);
                        }
                    }

                    lg.Add(pg);
                }
            }

            SPWeb site = new SPSite("http://" + tfs.Name + "/sites/" + ProjectName).OpenWeb();


            foreach (SPRoleDefinition gp in site.RoleDefinitions)
            {
                PsGroups pg = new PsGroups();
                pg.TypeMember = TypeMember.Wss;
                pg.AccountName = gp.Name;
                pg.Description = gp.Description;
                pg.DisplayName = gp.Name;
                pg.Members = new List<PsMember>();
                pg.Sid = gp.Id.ToString();

                foreach (SPUser u in site.SiteUsers)
                {
                    SPRoleCollection gc = u.Roles;
                    foreach (SPRole r in gc)
                    {
                        if (r.ID == gp.Id)
                        {
                            PsMember pm = new PsMember();
                            pm.AccountName = u.LoginName;
                            pm.Description = u.Xml;
                            pm.DisplayName = u.Name;
                            pm.MailAddress = u.Email;
                            pm.Sid = u.ID.ToString();
                            pg.Members.Add(pm);
                        }
                    }
                }

                lg.Add(pg);
            }
            return lg;
        }

        #endregion GetMembersProject

        #region AddBranche

        public static int AddBranche(string description, string Source, string Target, string ProjectName, TeamFoundationServer TFS)
        {
            int changesetNumber = 0;
            //Création du répertoire temporaire pour le workspace
            string tmp = Environment.GetFolderPath(Environment.SpecialFolder.InternetCache) + Guid.NewGuid().ToString();


            //Rajout des elements pour le nom du projet
            if (!ProjectName.Contains("$"))
            {
                ProjectName = @"$/" + ProjectName;
            }

            // Récupération du Version Control. 
            VersionControlServer versionControl = PsService.VCS(TFS);

            // Creation du workspace. 
            Workspace workspace = versionControl.CreateWorkspace(Guid.NewGuid().ToString(), versionControl.AuthenticatedUser);

            try
            {
                // Mapping du projet et du répertoire. 
                workspace.Map(ProjectName, tmp);

                // Récupération des fichier du serveur. 
                workspace.Get();

                workspace.PendBranch(Source, Target, VersionSpec.Latest);

                // Affiche les changement en cour. 
                PendingChange[] pendingChanges = workspace.GetPendingChanges();

                if (pendingChanges.Count() > 0)
                {
                    // archive les fichiers et récupère le changeset 
                    changesetNumber = workspace.CheckIn(pendingChanges, description);
                    PsLog.AddLog("Checked in changeset " + changesetNumber);
                }
            }
            finally
            {
                // Supprime le workspace. 
                workspace.Delete();
            }
            return changesetNumber;

        }

            #endregion AddBranche

        /// <summary>
        /// Permet d'ajouter les fichiers d'un répertoire
        /// </summary>
        /// <param name="tmp"></param>
        /// <param name="workspace"></param>
        /// <param name="_s"></param>
        /// <param name="sousrep"></param>
        private static void AddFile(string tmp, Workspace workspace, string _s, string sousrep, RecursionType Recursion)
        {
            //Parcour les fichiers
            foreach (string s in Directory.GetFiles(_s))
            {
                //Récupération du nom du fichier
                string name2 = System.IO.Path.GetFileName(s);

                string fileTarget = tmp + @"\" + sousrep + @"\" + name2;

                fileTarget = fileTarget.Replace(@"\\", @"\");

                string direc = System.IO.Path.GetDirectoryName(fileTarget);

                if (!Directory.Exists(direc))
                {
                    //Création du dossier si il n'existe pas
                    Directory.CreateDirectory(direc);
                }

                //Copie le fichier dans le workspace
                File.Copy(s, fileTarget, true);

                // Ajout du fichier en attente d'archivage. 
                workspace.PendAdd(fileTarget, (Recursion == RecursionType.Full));
            }
            if (Recursion == RecursionType.Full)
            {
                foreach (string d in Directory.GetDirectories(_s))
                {
                    // Récurcivité si il y a sous dossier
                    AddFile(tmp + @"\" + sousrep, workspace, d, d.Substring(_s.Length),Recursion);
                }
            }
        }

        public static void AddFiles(string tmp, string Target, string Source, TeamFoundationServer Tfs, string Description, RecursionType Recursion)
        {

            //Rajout des elements pour le nom du projet
            if (!Target.Contains("$"))
            {
                Target = @"$/" + Target;
            }

            // Récupération du Version Control. 
            VersionControlServer versionControl = PsService.VCS(Tfs);

            // Creation du workspace. 
            Workspace workspace = versionControl.CreateWorkspace(Guid.NewGuid().ToString(), versionControl.AuthenticatedUser);

            try
            {
                // Mapping du projet et du répertoire. 
                workspace.Map(Target, tmp);

                // Récupération des fichier du serveur. 
                workspace.Get();

                //Parcourt les fichiers a ajouter
                foreach (string _s in Source.Split(';'))
                {

                    string name = System.IO.Path.GetFileName(_s);
                    //Si pas de nom de fichier ajouter le répertoire complet
                    if (name == string.Empty)
                    {
                        AddFile(tmp, workspace, _s, "", Recursion);
                    }
                    else
                    {
                        string fileTarget = tmp + @"\" + @"\" + name;

                        string direc = System.IO.Path.GetDirectoryName(fileTarget);

                        if (!Directory.Exists(direc))
                        {
                            //Création du dossier destinataire
                            Directory.CreateDirectory(direc);
                        }

                        if (File.Exists(fileTarget))
                        {
                            // Ajout du fichier. 
                            workspace.PendEdit(fileTarget);
                            workspace.PendAdd(fileTarget);
                        }
                        else
                        {
                            //Copie du fichier dans le workspace
                            File.Copy(_s, fileTarget, true);
                            // Ajout du fichier. 
                            workspace.PendAdd(fileTarget, (Recursion == RecursionType.Full));
                        }

                    }
                }

                // Affiche les changement en cour. 
                PendingChange[] pendingChanges = workspace.GetPendingChanges();
                PsLog.AddLog("Your current pending changes:");
                //Parcour les fichier en attente
                foreach (PendingChange pendingChange in pendingChanges)
                {
                    PsLog.AddLog("  path: " + pendingChange.LocalItem +
                                      ", change: " + PendingChange.GetLocalizedStringForChangeType(pendingChange.ChangeType));
                }

                // archive les fichiers et récupère le changeset 
                int changesetNumber = workspace.CheckIn(pendingChanges, Description);
                PsLog.AddLog("Checked in changeset " + changesetNumber);
            }
            finally
            {
                // Supprime le workspace. 
                workspace.Delete();
            }
        }
    }
}
